(function ($) {
    'use strict';

    $.fn.JySelect = function (opts) {
        let options = $.extend(true, {}, defOpts, typeof opts === 'object' && opts);
        var jyselect = new JySelect(this, options);
        return jyselect;
    }

    var defOpts = {
        mode: "multiple",
        width: '100%',
        height: 'auto',
        key: null,
        value: null,
        check: true,
        view: {
            radius: true,
            theme: 'skyBlue',
            radius: true,
            separator: "，",
        },
        data: [],
        selected: [],
        placeholder: "请选择",
    };

    var JySelect = function (elemnt, opts) {
        this._initOptions(opts);
        this._create(elemnt);
        this._initEvt();
        this._mountFunction();
    }

    JySelect.prototype = {
        classes: {
            JY_SELECT_CONTAINER: "jy-select-container",
            JY_SELECT_INPUT: "jy-select-input",
            JY_SELECT_INPUT_RADIUS: "jy-select-input-radius",
            JY_SELECT_ICON: "jy-select-icon",
            JY_SELECT_PANEL: "jy-select-panel",
            JY_SELECT_PANEL_BORDER: "jy-select-panel-border",
            JY_SELECT_PANEL_EXPAND: "jy-select-panel-expand",
            JY_SELECT_ICON: "jy-select-icon",
            JY_SELECT_ICONFONT: "iconfont",
            JY_SELECT_ICON_UP: "icon-sanjiaojiantoushang",
            JY_SELECT_ICON_DWON: "icon-xialasanjiaojiantou",
            JY_SELECT_OPTIONS_CONTAINER: "jy-select-options-container",
            JY_SELECT_OPTION: "jy-select-option",
            JY_SELECTED: "selected",
            JY_OPTION_SELECTED_ACTIVE: "jy-option-selected-active",
            JY_SELECT_OPTION_ICON: "jy-select-option-icon",
            JY_SELECT_OPTION_ICON_NOCHECK: "icon-danxuankuang-weixuanze",
            JY_SELECT_OPTION_ICON_CHECK: "icon-danxuankuang-yixuanze",
            JY_SELECT_OPTION_CONTENT: "jy-select-option-content",
            JY_SELECT_DISABLED: "jy-select-disabled",
        },

        utils: {
            _isFunction: function (f) {
                return f && typeof f === "function";
            },
        },

        hooks: {},

        events: {
            beforeClick: function (val, ele, e) {
                this._excuteHooks("beforeClick", val, ele, e);
            },
            afterClick: function (val, ele, e) {
                this._excuteHooks("afterClick", val, ele, e);
            }
        },

        /**
         * 初始化入参
         * @param {*} opts 
         */
        _initOptions: function (opts) {
            this.$container = null;
            this.$input = null;
            this.$dropdown = null;
            this.$panel = null;
            this.$optionBox = null;
            this.expend = false;
            this.selected = [];
            this.selectedKey = [];
            this.options = opts;
            this.disabled = false;
        },

        /**
         * 创建入口
         * @param {*} elemnt 
         */
        _create: function (elemnt) {
            this._createContainer(elemnt);
            this._createInput();
            this._createDropDown();
            this._createPanle();
            this._createOptions();
        },

        /**
         * 创建容器
         * @param {*} elemnt 
         */
        _createContainer: function (elemnt) {
            let _this = this, classes = _this.__proto__.classes, opts = _this.options;
            let $container = $('<div>').attr("data-theme", opts.view.theme).addClass(classes.JY_SELECT_CONTAINER);
            elemnt.append($container);
            _this.$container = $container;
        },

        /**
         * 创建input
         */
        _createInput: function () {
            let _this = this, classes = _this.__proto__.classes, opts = _this.options;
            let $input = $("<input>").attr({ type: "text", placeholder: _this.placeholder, readonly: true })
                .addClass(classes.JY_SELECT_INPUT);
            opts.view.radius && $input.addClass(classes.JY_SELECT_INPUT_RADIUS);
            _this.$container.append($input);
            _this.$input = $input;
        },

        /**
         * 创建下拉按钮
         */
        _createDropDown: function () {
            let _this = this, classes = _this.__proto__.classes, opts = _this.options;
            let $dropdown = $("<span>").addClass(classes.JY_SELECT_ICON);
            $dropdown.addClass(classes.JY_SELECT_ICON).addClass(classes.JY_SELECT_ICONFONT)
                .addClass(classes.JY_SELECT_ICON_UP).css({ position: "absolute", top: "50%", transform: "translateY(-50%)", right: "15px" });
            _this.$container.append($dropdown);
            _this.$dropdown = $dropdown;
        },

        /**
         * 创建下来面板
         */
        _createPanle: function () {
            let _this = this, classes = _this.__proto__.classes, opts = _this.options;
            let $panel = $('<div>').addClass(classes.JY_SELECT_PANEL);
            $panel.css({ height: `0px` });
            _this.$container.append($panel);
            _this.$panel = $panel;
            _this._setPosition();
        },

        /**
         * 设置位置
         */
        _setPosition: function () {
            let _this = this;
            let inputHeight = _this.$container.height(), top = inputHeight + 3;
            this.$panel.css({
                top: `${top}px`,
            })
        },


        /**
         * 是否隐藏panel
         * @param {*} bool 
         */
        _isHiddenPanle: function () {
            let _this = this, classes = _this.__proto__.classes, opts = _this.options;
            if (_this.expend) {
                _this.$container.removeClass(classes.JY_SELECT_PANEL_EXPAND);
                _this.$panel.removeClass(classes.JY_SELECT_PANEL_BORDER).css({ height: "0px" });
                _this.$dropdown.removeClass(classes.JY_SELECT_ICON_DWON).addClass(classes.JY_SELECT_ICON_UP);
                _this.expend = false;
            } else {
                _this.$container.addClass(classes.JY_SELECT_PANEL_EXPAND)
                _this.$panel.addClass(classes.JY_SELECT_PANEL_BORDER).css({ height: opts.height });
                _this.$dropdown.removeClass(classes.JY_SELECT_ICON_UP).addClass(classes.JY_SELECT_ICON_DWON);
                _this.expend = true;
            }
        },

        /**
         * 创建下拉选项
         */
        _createOptions: function () {
            let _this = this, classes = _this.__proto__.classes, opts = _this.options;
            _this.$panel.html("");
            let $optionBox = $("<div>").addClass(classes.JY_SELECT_OPTIONS_CONTAINER);
            for (let option of opts.data) {
                let $option = $("<div>").addClass(classes.JY_SELECT_OPTION),
                    $optionIcon = $("<span>").addClass(classes.JY_SELECT_OPTION_ICON).addClass(classes.JY_SELECT_ICONFONT).addClass(classes.JY_SELECT_OPTION_ICON_NOCHECK),
                    $optionContent = $("<span>").addClass(classes.JY_SELECT_OPTION_CONTENT);
                $optionContent.text(option[opts.value]);
                opts.check && $option.append($optionIcon);
                const modeHandle = {
                    multiple: () => {
                        if (opts.selected.includes(option[opts.key])) {
                            _this.selected.push(option);
                            _this.selectedKey.push(option[opts.key]);
                            _this._addSeletedStyle($option);
                        }
                    },
                    signle: () => {
                        if (option[opts.key] == opts.selected[0]) {
                            _this.selected.push(option);
                            _this.selectedKey.push(option[opts.key]);
                            _this._addSeletedStyle($option);
                        }
                    }
                }
                modeHandle[opts.mode]();
                $option.append($optionContent);
                for (let key in option) {
                    $option.attr(`data-${key}`, option[key]);
                }
                $optionBox.append($option);
            };
            _this.$optionBox = $optionBox;
            _this.$panel.append($optionBox);
            _this._showValToInput();
        },

        /**
         * 初始化事件
         */
        _initEvt: function () {
            let _this = this, classes = _this.__proto__.classes;
            $(document).on("click", function (e) {
                let target = e.target, isDetail = target.closest(`.${classes.JY_SELECT_CONTAINER}`);
                if (isDetail || !_this.expend) return;
                _this._isHiddenPanle();
                _this.expend = false;
            });
            _this._inputEvt();
            _this._dropDownIconEvt();
            _this._optionEvt();
        },

        /**
         * input框事件
         */
        _inputEvt: function () {
            let _this = this, classes = _this.__proto__.classes;
            _this.$input.on("click", function (e) {
                if (_this.disabled) return;
                _this.expend = _this.$container.hasClass(classes.JY_SELECT_PANEL_EXPAND);
                _this._isHiddenPanle();
            });
        },

        /**
         * 下拉按钮事件
         */
        _dropDownIconEvt: function () {
            let _this = this, classes = _this.__proto__.classes;
            _this.$dropdown.on("click", function (e) {
                if (_this.disabled) return;
                _this.expend = _this.$container.hasClass(classes.JY_SELECT_PANEL_EXPAND);
                _this._isHiddenPanle();
                _this.expend && _this.$input.focus();
                e.preventDefault();
            });
        },

        /**
         * option点击事件
         */
        _optionEvt: function () {
            let _this = this, classes = _this.__proto__.classes, opts = _this.options, events = _this.events;
            _this.$optionBox.on("click", `.${this.classes.JY_SELECT_OPTION}`, function (e) {
                let $optionEle = $(e.target.closest(`.${classes.JY_SELECT_OPTION}`)),
                    isSelected = $optionEle.hasClass(classes.JY_SELECTED),
                    optionData = _this._getDataByOption($optionEle),
                    keyVal = _this._getOptionKeyVal($optionEle);
                events.beforeClick.call(_this, optionData, $optionEle[0], e);
                const modeHandle = {
                    multiple: () => {
                        if (isSelected) {
                            _this.selectedKey = _this.selectedKey.filter(item => { return item !== keyVal });
                            _this.selected = _this.selected.filter(item => { return item[opts.key] != keyVal });
                        } else {
                            _this.selected.push(optionData);
                            _this.selectedKey.push(keyVal);
                        }
                    },
                    signle: () => {
                        _this.selected = [];
                        _this.selectedKey = [];
                        if (!isSelected) {
                            _this.selected.push(optionData);
                            _this.selectedKey.push(keyVal);
                        }
                    }
                };
                modeHandle[opts.mode]();
                _this._updateSelected();
                _this._showValToInput();
                events.afterClick.call(_this, optionData, $optionEle[0], e);
            });
        },

        /**
         * 更新选中样式
         */
        _updateSelected: function () {
            let _this = this, classes = _this.__proto__.classes, opts = _this.options;
            let $options = _this.$optionBox.find(`.${classes.JY_SELECT_OPTION}`);
            for (let i = 0; i < $options.length; i++) {
                let $option = $($options[i]);
                let _keyVal = _this._getOptionKeyVal($option);
                if (_this.selectedKey.includes(_keyVal)) {
                    _this._addSeletedStyle($option);
                } else {
                    _this._cancelSelectedStyle($option);
                }
            }
        },

        /**
         * input显示值
         */
        _showValToInput: function () {
            let _this = this, opts = _this.options, view = _this.options.view;
            let showVal = '';
            const modeHandle = {
                multiple: () => {
                    for (let s of _this.selected) {
                        showVal += showVal ? `${view.separator}${s[opts.value]}` : s[opts.value];
                    }
                },
                signle: () => {
                    showVal = _this.selected[0][opts.value];
                }
            };
            _this.selected.length && modeHandle[opts.mode]();
            _this.$input.val(showVal);
        },

        /**
         * 取消全部选中样式
         */
        _cancelAllSelectedStyle: function () {
            let _this = this, opts = _this.options, classes = _this.__proto__.classes;
            _this.$optionBox.find(`.${classes.JY_SELECT_OPTION}`).removeClass(classes.JY_SELECTED);
            if (opts.check) {
                _this.$optionBox.find(`.${classes.JY_SELECT_OPTION_ICON}`)
                    .removeClass(classes.JY_SELECT_OPTION_ICON_CHECK)
                    .addClass(classes.JY_SELECT_OPTION_ICON_NOCHECK);
            } else {
                _this.$optionBox.find(`.${classes.JY_SELECT_OPTION}`)
                    .removeClass(classes.JY_OPTION_SELECTED_ACTIVE);
            }
        },

        /**
         * 取消选中样式
         * @param {} $option 
         */
        _cancelSelectedStyle: function ($option) {
            let _this = this, opts = _this.options, classes = _this.__proto__.classes;
            $option.removeClass(classes.JY_SELECTED);
            setTimeout(() => {
                if (opts.check) {
                    $option.find(`.${classes.JY_SELECT_OPTION_ICON}`)
                        .removeClass(classes.JY_SELECT_OPTION_ICON_CHECK)
                        .addClass(classes.JY_SELECT_OPTION_ICON_NOCHECK);
                } else {
                    $option.removeClass(classes.JY_OPTION_SELECTED_ACTIVE);
                }
            });
        },

        /**
         * 添加选中样式
         * @param {*} $option 
         */
        _addSeletedStyle: function ($option) {
            let _this = this, opts = _this.options, classes = _this.__proto__.classes;
            $option.addClass(classes.JY_SELECTED);
            setTimeout(() => {
                if (opts.check) {
                    $option.find(`.${classes.JY_SELECT_OPTION_ICON}`)
                        .removeClass(classes.JY_SELECT_OPTION_ICON_NOCHECK)
                        .addClass(classes.JY_SELECT_OPTION_ICON_CHECK);
                } else {
                    $option.addClass(classes.JY_OPTION_SELECTED_ACTIVE);
                }
            });
        },

        /**
         * 通过$option获取数据
         * @param {*} $option 
         * @returns 
         */
        _getDataByOption: function ($option) {
            let _this = this, keyVal = _this._getOptionKeyVal($option);
            return _this._getDataByKeyVal(keyVal);
        },

        /**
         * 通过主键值获取该节点数据
         * @param {String} keyVal 
         */
        _getDataByKeyVal: function (keyVal) {
            let _this = this, opts = _this.options, data = opts.data.filter(item => { return item[opts.key] == keyVal });
            return data[0];
        },

        /**
         * 获取option的主键值
         * @param {*} $option 
         * @returns 
         */
        _getOptionKeyVal: function ($option) {
            let _this = this;
            return $option.attr(`data-${_this.options.key}`);
        },

        /**
         * 通过属性获取值
         * @param {*} $option 
         * @param {*} property 
         * @returns 
         */
        _getPropertyVal: function ($option, property) {
            return $option.attr(`data-${property}`);
        },

        /**
         * 获取选中数据
         */
        _getSelected: function () {
            let _this = this, opts = _this.options;
            const modeHandle = {
                multiple: () => {
                    return _this.selected;
                },
                signle: () => {
                    return _this.selected[0]
                }
            };
            return modeHandle[opts.mode]();
        },

        /**
         * 清除选中
         */
        _clearSelected: function () {
            let _this = this;
            _this.selected = [];
            _this.selectedKey = [];
            _this.$input.val("");
            _this._updateSelected();
        },

        /**
         * 设置数据
         * @param {Array} data 
         */
        _setData: function (data) {
            let _this = this, opts = _this.options;
            opts.data = data;
            _this._clearSelected();
            _this._createOptions();
        },


        /**
         * 设置选中
         * @param {*} keys 
         */
        _select: function (keys) {
            let _this = this, opts = _this.options, _keys = [];
            Array.isArray(keys) ? _keys.push(...keys) : _keys.push(keys);
            if (opts.mode == "signle") {
                _this.selected = [];
                _this.selectedKey = [];
            }
            for (let d of opts.data) {
                let keyVal = d[opts.key];
                if (_keys.includes(keyVal)) {
                    !_this.selected.includes(d) && _this.selected.push(d);
                    !_this.selectedKey.includes(keyVal) && _this.selectedKey.push(keyVal);
                }
            }
            _this._updateSelected();
        },

        /**
         * 添加数据,传入index可在指定位置插入
         * @param {*} data 
         * @param {*} index 
         */
        _addData(data, index) {
            let _this = this, opts = _this.options, _addData = [];
            Array.isArray(data) ? _addData.push(...data) : _addData.push(data);
            index ? opts.data.splice(index, 0, ..._addData)
                : opts.data.push(..._addData);
            _this._clearSelected();
            _this._createOptions();
        },

        /**
         * 销毁
         */
        _destroy: function () {
            this.$container.remove();
            for (let a in this) {
                delete this[a];
            };
        },

        /**
         * 获取全部数据
         * @returns 
         */
        _getAllData: function () {
            return this.options.data;
        },

        /**
         * 
         */
        _getDisplayValue: function () {
            return this.$input.val();
        },

        /**
         * 是否禁用
         * @param {*} bool 
         */
        _setDisable: function (bool) {
            let _this = this, classes = _this.classes;
            _this.disabled = bool;
            bool ? this.$input.addClass(classes.JY_SELECT_DISABLED)
                : this.$input.removeClass(classes.JY_SELECT_DISABLED);
            bool && _this._isHiddenPanle(true);
        },

        _addHooks: function (name, f) {
            let _this = this, utils = _this.utils;
            if (!utils._isFunction(f) || _this.hooks[name]) return;
            this.hooks[name] = f;
        },

        /**
         * 执行钩子函数
         * @param {*} name 
         * @param {*} args 
         */
        _excuteHooks: function (name, ...args) {
            this.hooks[name] && this.hooks[name](this, args);
        },

        /**
         * 挂载函数到实例对象
         */
        _mountFunction: function () {
            let _this = this;
            _this.getSelected = _this._getSelected;
            _this.clearSelected = _this._clearSelected;
            _this.setData = _this._setData;
            _this.addData = _this._addData;
            _this.destroy = _this._destroy;
            _this.select = _this._select;
            _this.getAllData = _this._getAllData;
            _this.getDisplayValue = _this._getDisplayValue;
            _this.addHooks = _this._addHooks;
            _this.setDisable = _this._setDisable;
        },
    }
})(window.jQuery)